env 配置文件加载
简介
接着上一章所说,这一章介绍 .env
文件的引导加载。
- 如图,
Illuminate\Foundation\Http\Kernel
类属性$bootstrappers
中第一个引导启动类
Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables
类,从字面意义不难看出,就是有关env
配置文件加载
正式介绍
如图,从 Illuminate\Foundation\Application
容器的 bootstrapWith
方法开始:
上一章我讲过,Laravel 的配置加载其实就是实例化相关类,并调用相关类的 bootstrap
方法,在这一章这个相关类就是 Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables
接下来我们进入 Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables
的 bootstrap
中看一下
public function bootstrap(Application $app)
{
if ($app->configurationIsCached()) {
return;
}
$this->checkForSpecificEnvironmentFile($app);
try {
(new Dotenv($app->environmentPath(), $app->environmentFile()))->load(); // 核心
} catch (InvalidPathException $e) {
//
} catch (InvalidFileException $e) {
die('The environment file is invalid: '.$e->getMessage());
}
}
从这个方法的代码上,不难看出,程序先检测有没有配置缓存,配置缓存就是 bootstrap/cache/config.php
,如果有这个文件,则退出加载,否则继续;接着检测特殊配置文件。
关于检测特殊配置文件的内容,我在这简单一说,不贴出代码了,这块不是重点:
检测特殊配置,首先是检测当前执行环境是 web
访问还是 cli
执行,如果是 cli
执行,如果命令行中含有 --env
,则设置配置文件路径,此功能结束;之后,通过 env
函数读取 APP_ENV
键,看是否有返回值,如果没有返回值,则结束检测,若果有返回值,将系统默认的 .env
文件路径与返回值拼接,设置到 env
配置路径中。
我现在执行的环境,用的的 web
访问,且 env('APP_ENV')
无返回值,即 .env
配置文件的路径为系统默认路径
下面我们看一下 env
配置加载核心代码 (new Dotenv($app->environmentPath(), $app->environmentFile()))->load();
这段代码指,实例化 Dotenv
将 .env
文件所在绝对路径以及文件名当做参数传入,然后调用 load
方法
如图,实例化 Dotenv
时的构造程序
$this->getFilePath($path, $file)
作用就是将路径和文件名拼接起来,构成完整的 .env
文件的绝对路径;然后实例化 Loader
类,将完整的 .env
文件路径传入,外加一个 true
;取得 Loader
类的实例对象后,赋值给当前对象的 loader
属性
下面我们看一下 Loader
类的构造函数
public function __construct($filePath, $immutable = false)
{
$this->filePath = $filePath;
$this->immutable = $immutable;
}
不难看出,就是一个简单的属性赋值运算。
好了,整个 Dotenv
类的实例化结束,下面看一下它的 load
方法
public function load()
{
return $this->loadData();
}
调用了 loadData
方法,继续看
protected function loadData($overload = false)
{
return $this->loader->setImmutable(!$overload)->load();
}
调用了 Loader
类下的 setImmutable
方法,并传入 false
,看一下这个 setImmutable
方法
public function setImmutable($immutable = false)
{
$this->immutable = $immutable;
return $this;
}
将 false
赋值给 immutable
属性,返回了 Loader
类对象
那么,我们就看一下 Loader
类对象下的 load
方法
public function load()
{
$this->ensureFileIsReadable();
$filePath = $this->filePath;
$lines = $this->readLinesFromFile($filePath);
foreach ($lines as $line) {
if (!$this->isComment($line) && $this->looksLikeSetter($line)) {
$this->setEnvironmentVariable($line);
}
}
return $lines;
}
呼!走了这么长的路,终于要到了。。。
$this->ensureFileIsReadable();
作用是检测.env
文件是否存在,是否可读,可以时通过,不可以时,抛出异常,结束程序;$lines = $this->readLinesFromFile($filePath);
作用是根据.env
配置文件的换行符,将每一行的字符串叠加到一个索引数组中,并返回该数组,我们看一下这个方法
protected function readLinesFromFile($filePath)
{
// Read file into an array of lines with auto-detected line endings
$autodetect = ini_get('auto_detect_line_endings');
ini_set('auto_detect_line_endings', '1');
$lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
ini_set('auto_detect_line_endings', $autodetect);
return $lines;
}
其中 PHP 的配置项 auto_detect_line_endings
,看下图
意思是说,当调用 file
方法的时候,自动检测换行符。因为有性能损失,所以,先 ini_get
一下,用完,再设置回去。
关于 file
方法,看这里
最后给你看一下,读取完 .env
文件变量情况
我们回到 load
方法继续
foreach ($lines as $line) {
if (!$this->isComment($line) && $this->looksLikeSetter($line)) {
$this->setEnvironmentVariable($line);
}
}
这段循环值,将 $lines
数组每个元素进行解析,并赋值到系统环境变量中。
详解:
!$this->isComment($line)
指如果当前元素第一个字符有值并且是 #
代表 false
,不执行 setEnvironmentVariable
方法,这相当于是一个注释;具体看代码
protected function isComment($line)
{
$line = ltrim($line);
return isset($line[0]) && $line[0] === '#'; // 在这里哦,返回 boolean
}
$this->looksLikeSetter($line)
指在当前元素中有没有 =
号,有则返回 true
,看代码
protected function looksLikeSetter($line)
{
return strpos($line, '=') !== false;
}
通过上面两个判断,就是说如果当前元素开头不是 #
,且含有 =
则,执行 setEnvironmentVariable
方法
我们看一下 setEnvironmentVariable
方法
public function setEnvironmentVariable($name, $value = null)
{
list($name, $value) = $this->normaliseEnvironmentVariable($name, $value);
$this->variableNames[] = $name;
// Don't overwrite existing environment variables if we're immutable
// Ruby's dotenv does this with `ENV[key] ||= value`.
if ($this->immutable && $this->getEnvironmentVariable($name) !== null) {
return;
}
// If PHP is running as an Apache module and an existing
// Apache environment variable exists, overwrite it
if (function_exists('apache_getenv') && function_exists('apache_setenv') && apache_getenv($name)) {
apache_setenv($name, $value);
}
if (function_exists('putenv')) {
putenv("$name=$value");
}
$_ENV[$name] = $value;
$_SERVER[$name] = $value;
}
未调用 normaliseEnvironmentVariable
前,name
就是 $lines
一个元素(包含键和值的字符串), value
是 null
。调用 normaliseEnvironmentVariable
之后,程序会根据元素中的 =
进行键值分割,分别赋值给 $name
和 $value
。
关于 normaliseEnvironmentVariable
方法,实现了元素以 =
分解键值对功能,识别值中加密串和分解值中后面的 #
注释的能力,识别 键和值 中变量的能力,最终解析出需要的内容,分别赋值给 $name
和 $value
之后检测 PHP 内置是否存在一些函数,有则调用,并将获取的键和值作为参数传入,下面看一下 putenv
函数
if (function_exists('putenv')) {
putenv("$name=$value");
}
我执行的环境中是有这个函数的,这是官方对于这个函数讲解
最后,进行了 $_ENV
和 $_SERVER
赋值
$_ENV[$name] = $value;
$_SERVER[$name] = $value;
文章末尾贴一张,加载完 .env
的变量图
写在文章尾
通过上面的学习,我们知道了。 .env
文件是支持注释的,注释的开头是 #
,仅能支持一行,另一行也以 #
开头。还可以下载当前的行尾。如下
下一章讲解 config
配置文件的加载